package io.github.echarts.model.coordinate.axis;

import io.github.echarts.model.coordinate.DataRange;
import io.github.echarts.option.component.Axis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 坐标轴模型，负责处理坐标轴的数据和刻度生成
 */
public class AxisModel {
    /**
     * 坐标轴配置
     */
    private Axis axis;
    
    /**
     * 数据范围
     */
    private DataRange dataRange;
    
    /**
     * 类别数据
     */
    private List<Object> categories;
    
    /**
     * 刻度分隔数量
     */
    private int splitNumber = 5;
    
    /**
     * 创建坐标轴模型
     * 
     * @param axis 坐标轴配置
     */
    public AxisModel(Axis axis) {
        this.axis = axis;
        initialize();
    }
    
    /**
     * 初始化坐标轴模型
     */
    private void initialize() {
        // 如果是类别轴，初始化类别数据
        if ("category".equals(axis.getType()) && axis.getData() != null) {
            this.categories = Arrays.asList(axis.getData());
        } else {
            this.categories = new ArrayList<>();
        }
        
        // 初始化数据范围
        initDataRange();
    }
    
    /**
     * 初始化数据范围
     */
    private void initDataRange() {
        double min = 0;
        double max = 100;
        
        // 使用轴配置中的最小值和最大值（如果有）
        if (axis.getMin() instanceof Number) {
            min = ((Number) axis.getMin()).doubleValue();
        }
        
        if (axis.getMax() instanceof Number) {
            max = ((Number) axis.getMax()).doubleValue();
        }
        
        // 确保min和max不相等，避免刻度生成问题
        if (Math.abs(max - min) < 0.000001) {
            if (min == 0) {
                max = 100;  // 如果min=0且等于max，设置max为100
            } else {
                // 扩展10%的范围
                double delta = Math.abs(min * 0.1);
                if (delta < 0.000001) delta = 1.0;  // 避免delta太小
                min = min - delta;
                max = max + delta;
            }
        }
        
        this.dataRange = new DataRange(min, max);
    }
    
    /**
     * 获取轴的数据范围
     * 
     * @return 数据范围
     */
    public DataRange getDataRange() {
        return dataRange;
    }
    
    /**
     * 设置轴的数据范围
     * 
     * @param dataRange 数据范围
     */
    public void setDataRange(DataRange dataRange) {
        this.dataRange = dataRange;
    }
    
    /**
     * 更新数据范围，根据传入的系列数据
     * 
     * @param seriesDataRange 系列数据范围
     */
    public void updateDataRange(DataRange seriesDataRange) {
        if (seriesDataRange == null) {
            return;
        }
        
        // 如果用户设置了固定范围，则不更新
        if (axis.getMin() != null && axis.getMax() != null) {
            return;
        }
        
        // 如果当前范围为空，直接使用系列范围
        if (dataRange == null) {
            this.dataRange = seriesDataRange;
            return;
        }
        
        // 否则，合并范围
        if (axis.getMin() == null) {
            dataRange.setMin(Math.min(dataRange.getMin(), seriesDataRange.getMin()));
        }
        
        if (axis.getMax() == null) {
            dataRange.setMax(Math.max(dataRange.getMax(), seriesDataRange.getMax()));
        }
    }
    
    /**
     * 生成刻度值列表
     * 
     * @return 刻度值列表
     */
    public List<Double> generateTicks() {
        List<Double> ticks = new ArrayList<>();
        
        // 对于类别轴，返回空列表，因为类别轴的刻度是类别本身
        if ("category".equals(axis.getType())) {
            return ticks;
        }
        
        double min = dataRange.getMin();
        double max = dataRange.getMax();
        
        // 如果min和max相等，扩展一下范围
        if (Math.abs(max - min) < 0.000001) {
            if (min == 0) {
                // 如果是0，使用0-10范围
                min = 0;
                max = 10;
            } else {
                // 否则在当前值基础上扩展
                double delta = Math.abs(min * 0.1);
                if (delta < 0.00001) delta = 1.0; // 避免非常小的值
                min = min - delta;
                max = max + delta;
            }
        }
        
        // 计算步长
        double step = (max - min) / splitNumber;
        
        // 美化步长，使其为5的倍数、2的倍数或10的幂
        step = prettyStep(step);
        
        // 调整min和max为步长的整数倍
        min = Math.floor(min / step) * step;
        max = Math.ceil(max / step) * step;
        
        // 重新计算步长，确保splitNumber附近的刻度数
        int actualSplitNumber = (int) Math.round((max - min) / step);
        if (actualSplitNumber > splitNumber * 2) {
            // 如果刻度太多，增大步长
            step = step * 2;
            actualSplitNumber = (int) Math.round((max - min) / step);
        } else if (actualSplitNumber < splitNumber / 2 && actualSplitNumber > 0) {
            // 如果刻度太少，减小步长
            step = step / 2;
        }
        
        // 生成刻度值
        for (double value = min; value <= max + step * 0.1; value += step) {
            // 添加刻度，加上一个小的容差值避免浮点精度问题
            ticks.add(value);
        }
        
        return ticks;
    }
    
    /**
     * 将步长美化为更易于阅读的值
     * 
     * @param step 原始步长
     * @return 美化后的步长
     */
    private double prettyStep(double step) {
        // 获取数量级
        double magnitude = Math.pow(10, Math.floor(Math.log10(step)));
        double normalized = step / magnitude; // 1.0-9.999...
        
        // 选择最接近的美化数字
        if (normalized < 1.5) {
            return magnitude;        // 1
        } else if (normalized < 3) {
            return 2 * magnitude;    // 2
        } else if (normalized < 7) {
            return 5 * magnitude;    // 5
        } else {
            return 10 * magnitude;   // 10
        }
    }
    
    /**
     * 格式化刻度标签
     * 
     * @param value 刻度值
     * @return 格式化后的标签文本
     */
    public String formatLabel(double value) {
        // TODO: 支持自定义格式化
        
        // 如果是整数，则不显示小数部分
        if (Math.abs(value - Math.floor(value)) < 0.000001) {
            return String.format("%.0f", value);
        }
        
        // 否则显示至多2位小数
        return String.format("%.2f", value);
    }
    
    /**
     * 获取类别数据
     * 
     * @return 类别数据列表
     */
    public List<Object> getCategories() {
        return categories;
    }
    
    /**
     * 获取类别数量
     * 
     * @return 类别数量
     */
    public int getCategoryCount() {
        return categories != null ? categories.size() : 0;
    }
    
    /**
     * 判断是否为类别轴
     * 
     * @return 如果是类别轴返回true，否则返回false
     */
    public boolean isCategoryAxis() {
        return "category".equals(axis.getType());
    }
    
    /**
     * 获取指定索引的类别
     * 
     * @param index 类别索引
     * @return 类别对象
     */
    public Object getCategory(int index) {
        if (categories != null && index >= 0 && index < categories.size()) {
            return categories.get(index);
        }
        return null;
    }
    
    /**
     * 获取类别的索引
     * 
     * @param category 类别对象
     * @return 索引值，如果不存在则返回-1
     */
    public int getCategoryIndex(Object category) {
        if (categories != null) {
            return categories.indexOf(category);
        }
        return -1;
    }
    
    /**
     * 获取坐标轴配置
     * 
     * @return 坐标轴配置
     */
    public Axis getAxis() {
        return axis;
    }
    
    /**
     * 设置刻度分隔数量
     * 
     * @param splitNumber 刻度分隔数量
     */
    public void setSplitNumber(int splitNumber) {
        if (splitNumber > 0) {
            this.splitNumber = splitNumber;
        }
    }
    
    /**
     * 获取刻度分隔数量
     * 
     * @return 刻度分隔数量
     */
    public int getSplitNumber() {
        return splitNumber;
    }
} 